package me.cabbit.digitone.tones.halftone;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;

import org.w3c.dom.Element;

import me.cabbit.digitone.support.Strings;

/**
 * Options panel for the {@link Halftone} screentone. Lets you load
 * an arbitrary image and set all the necessary parameters
 * 
 * @author Arkaid
 *
 */
public class HalftonePanel extends Halftone implements ChangeListener, ActionListener, ItemListener {

	//------------------------------------------------------------------//
	//--------------------------- properties ---------------------------//
	//------------------------------------------------------------------//

	/** Options panel. This gets added to the main window's toolbox */	
	private JPanel panel;

	/** Strings for internationalization */	
	private Strings strings;
		
	/** Min dot size control */	
	private JSpinner spnMinSize;

	/** Max dot size control */	
	private JSpinner spnMaxSize;

	/** Horizontal spacing control */	
	private JSpinner spnHSpacing;

	/** Vertical spacing control */	
	private JSpinner spnVSpacing;

	/** angle control */	
	private JSpinner spnAngle;

	/** Color invert control */	
	private JCheckBox chkInvert;
	
	/** A flag to indicate a preset is being loaded. Used to
	 * temporarily refresh the interface without triggering
	 * events
	 */
	private boolean isLoadingPreset;
	
	//------------------------------------------------------------------//
	//----------------------------- methods ----------------------------//
	//------------------------------------------------------------------//

	/**
	 * Constructor. Add all the controls to the panel
	 * and start with a given sample image
	 * 
	 * @throws FileNotFoundException if the strings file is not found
	 */
	public HalftonePanel() throws FileNotFoundException {
		//load strings
		this.strings = new Strings("lang/Halftone.strings");
		Strings str = this.strings;
	
		//build panel
		this.panel = new JPanel();
		
		//set layout and constraints
		//Set layout and prepare constraints
		this.panel.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		c.insets = new Insets(3, 3, 3, 3);
		c.fill = GridBagConstraints.HORIZONTAL;

		//label for the filename
		c.gridx = 0; c.gridy=0; c.weightx = 1.0; c.gridwidth = 2;
		JButton btn = new JButton(str.get("OPTION_BROWSE"));
		btn.setActionCommand("OPTION_BROWSE");
		btn.addActionListener(this);
		this.panel.add(btn, c);			
		
		//min dotsize
		c.gridx = 0; c.gridy++; c.weightx = 0.25; c.gridwidth = 1;
		this.panel.add(new JLabel(str.get("OPTION_MIN_RADIUS")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.spnMinSize = new JSpinner(new SpinnerNumberModel(1, 0, null, 1));
		this.spnMinSize.setValue(this.getMinSize());
		this.spnMinSize.addChangeListener(this);
		this.panel.add(this.spnMinSize, c);
		
		//max dotsize
		c.gridx = 0; c.gridy++; c.weightx = 0.25;
		this.panel.add(new JLabel(str.get("OPTION_MAX_RADIUS")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.spnMaxSize = new JSpinner(new SpinnerNumberModel(1, 0, null, 1));
		this.spnMaxSize.setValue(this.getMaxSize());
		this.spnMaxSize.addChangeListener(this);
		this.panel.add(this.spnMaxSize, c);		
		
		//horizontal spacing
		c.gridx = 0; c.gridy++; c.weightx = 0.25;
		this.panel.add(new JLabel(str.get("OPTION_HORIZONTAL_SPACING")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.spnHSpacing = new JSpinner(new SpinnerNumberModel(0, 0, null, 1));
		this.spnHSpacing.setValue(this.getHorizontalSpacing());
		this.spnHSpacing.addChangeListener(this);
		this.panel.add(this.spnHSpacing, c);
		
		//vertical spacing
		c.gridx = 0; c.gridy++; c.weightx = 0.25;
		this.panel.add(new JLabel(str.get("OPTION_VERTICAL_SPACING")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.spnVSpacing = new JSpinner(new SpinnerNumberModel(0, 0, null, 1));
		this.spnVSpacing.setValue(this.getVerticalSpacing());
		this.spnVSpacing.addChangeListener(this);
		this.panel.add(this.spnVSpacing, c);	
		
		//angle
		c.gridx = 0; c.gridy++; c.weightx = 0.25;
		this.panel.add(new JLabel(str.get("OPTION_ANGLE")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.spnAngle = new JSpinner(new SpinnerNumberModel(0, 0, 90, 1));
		this.spnAngle.setValue(this.getAngle());
		this.spnAngle.addChangeListener(this);
		this.panel.add(this.spnAngle, c);
		//invert colors
		c.gridx = 0; c.gridy++; c.weightx = 0.25;
		this.panel.add(new JLabel(str.get("OPTION_INVERT_COLORS")), c);
		
		c.gridx = 1; c.weightx = 0.75;
		this.chkInvert = new JCheckBox();
		this.chkInvert.setSelected(this.getInvertColors());
		this.chkInvert.addItemListener(this);
		this.panel.add(this.chkInvert, c);
		
		//empty panel to push everything to the top
		c.gridx = 0; c.gridy++; c.weighty = 1; 
		this.panel.add(new JPanel(), c);

		this.isLoadingPreset = false;
		
		try {
			this.setSource(ImageIO.read(new File("res/halftone_sample.jpg")));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	public void loadPreset(Element node) {
		super.loadPreset(node);
		
		this.isLoadingPreset = true;

		this.spnMinSize.setValue(this.getMinSize());
		this.spnMaxSize.setValue(this.getMaxSize());
		this.spnHSpacing.setValue(this.getHorizontalSpacing());
		this.spnVSpacing.setValue(this.getVerticalSpacing());
		this.spnAngle.setValue(this.getAngle());
		this.chkInvert.setSelected(this.getInvertColors());
		
		this.isLoadingPreset = false;
	}
	
	/**
	 * Shows a file load dialog with a filter for JPG and PNG images
	 * 
	 * @return the selected file, null if cancelled
	 */
	protected File showLoadDialog()
	{
		//prepare the file chooser
		JFileChooser dlg = new JFileChooser();
		dlg.setDialogType(JFileChooser.OPEN_DIALOG);
		
		//set the filters
		dlg.addChoosableFileFilter(new FileFilter() {
			@Override
			public String getDescription() {
				return "JPEG File";
			}
			
			@Override
			public boolean accept(File f) {
				return f.getName().endsWith(".jpg");
			}
		});		
		
		dlg.addChoosableFileFilter(new FileFilter() {
			@Override
			public String getDescription() {
				return "PNG File";
			}
			
			@Override
			public boolean accept(File f) {
				return f.getName().endsWith(".png");
			}
		});		
		
		dlg.addChoosableFileFilter(new FileFilter() {
			@Override
			public String getDescription() {
				return "Image files";
			}
			
			@Override
			public boolean accept(File f) {
				return f.getName().endsWith(".png") ||  f.getName().endsWith(".jpg");
			}
		});		
				
		//if the user cancelled, return null
		if (dlg.showDialog(this.panel.getParent(), "Load") != JFileChooser.APPROVE_OPTION)
			return null;

		//return the file
		return dlg.getSelectedFile();
	}
	
	@Override
	public JPanel getPanel() {
		return this.panel;
	}

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return this.strings.get("SCREENTONE_NAME");
	}

	@Override
	public String getUID() {
		return "AB54C2485E2D4343A43164A441198709";
	}

	@Override
	public void stateChanged(ChangeEvent event) {
		
		if (this.isLoadingPreset)
			return;
		
		//according to who raised the event, update the correct property
		
		//min dot size
		if (event.getSource() == this.spnMinSize)
			this.setMinSize((int)this.spnMinSize.getValue());
		
		//max dot size
		else if (event.getSource() == this.spnMaxSize)
			this.setMaxSize((int)this.spnMaxSize.getValue());
	
		//horizontal spacing
		else if (event.getSource() == this.spnHSpacing) 
			this.setHorizontalSpacing((int)this.spnHSpacing.getValue());
		
		//vertical spacing
		else if (event.getSource() == this.spnVSpacing)
			this.setVerticalSpacing((int)this.spnVSpacing.getValue());
	
		//angle
		else if (event.getSource() == this.spnAngle)
			this.setAngle((int)this.spnAngle.getValue());
	
		//alert of properties changed (refresh display)
		this.raisePropertyChangedEvent();	
	}
	

	@Override
	public void actionPerformed(ActionEvent e) 
	{
		//Shows the dialog
		File f = showLoadDialog();
		
		//if a file was selected, set it as source
		if (f!=null) 
		{
			try {
				this.setSource(ImageIO.read(f));
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			raisePropertyChangedEvent();
		}
		
	}

	@Override
	public void itemStateChanged(ItemEvent event) {
		//invert colors
		if (event.getSource() == this.chkInvert)
			this.setInvertColors(this.chkInvert.isSelected());
	
		//alert of properties changed (refresh display)
		this.raisePropertyChangedEvent();	
	}
}
